Skip to content

fix(lark): deliver blocked Todo notices through Goal Channel - #5452

Merged
huangruiteng merged 11 commits into
loopx-project:mainfrom
jackie-cqz:codex/fix-blocked-notice-delivery
Oct 2, 2026
Merged

huangruiteng merged 11 commits into
loopx-project:mainfrom
jackie-cqz:codex/fix-blocked-notice-delivery

Conversation

@jackie-cqz

@jackie-cqz jackie-cqz commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem and result

Refs #4381. Canonical blocked Todo notices can now be delivered through an explicitly enabled Lark Goal Channel. Configure the independent default-off setting from the existing CLI, Chat API or Goal capability editor, then read back current-target delivery counts while safe fallback work continues.

  • Require the configured bot/channel and existing extension, identity and membership boundaries. Keep provider receipts private and Todo recovery under the canonical status/notice owner.
  • Bind deduplication to blocker identity, revision, destination and durable generation. Explicit open recovery records resumed; terminal/superseding facts retire old receipts separately. Missing or incomplete rows do not prove recovery. Repeated same-clock recovery and return to an earlier cause generate new deliveries.
  • Keep the full candidate frontier. Attempt at most eight pending effects per refresh, prioritizing unattempted effects before retries. Already verified receipts consume no effect budget. Persist deferred pending receipts and report partial batches truthfully.
  • Direct and named target changes require independent send/readback in the new channel while preserving old history. Public delivery totals exclude old destinations and retired receipts.
  • Default-off and suppressed delivery preserve binding bytes and do not invoke transport. Human-gate-only status reads do not enter blocked-notice target lookup. Human-gate settings remain independent.

The bounded refactor concentrates receipt reconciliation and selection in the existing Lark delivery owner, shares destination matching with its public read model, and reuses canonical Todo normalization and notice composition. It introduces no parallel Todo authority or new capability.

Validation

  • New semantic regressions reproduce the original recovery, batch starvation and cross-channel failures before the fix. Real temporary binding IO with synthetic message transport now covers repeated recovery, 17 blockers across multiple refreshes, persistent send failures, direct/named channel switches, supersession, public readback and default-off isolation.
  • Final Linux notice/Goal Channel/shared-notice/census/module-budget matrix: 142 passed. Windows executes the same matrix with 139 passed and three existing POSIX 0600 assertions failing on that platform; all three pass on Linux.
  • Packaged Chat build and browser scenario pass on latest main: default-off display, failed-write correction, enable/readback, mobile reload, keyboard disable, independent human-gate setting and active/pending/retired counts. Existing settings layout and navigation are reused.
  • Ruff, diff checks and public-boundary scan pass. Registry I/O census has 272 classified sites; the 147-module budget is unchanged.
  • Live Lark transport and external group readback remain unqualified; no live group is claimed from synthetic transport evidence.

Current CI repairs and qualification

Tested head: 99155a77d38bd023d8a5243bee118e3a8bdb0c0e. Base: 4fc30f185 (includes #5467). Local run state: finished; inputs: synthetic and public fixtures. Earlier host/provider/browser evidence above retains its stated scope. Hosted CI is rerunning.

  • The shared release build failed because its uninstall assertion omitted the newly shipped performance-diagnosis skill. Rebase includes upstream fix(release): align artifact and host validation with shipped contracts #5467, which validates the removed set against the ready installed readback.
  • Upstream owns the presentation-module move. The remaining shared fixture repairs preserve UTF-8 activity readback, the interrupted-Turn read-only contract and Windows install/update boundaries.
Check Result Scope
Kernel type check passed Python 3.11, mypy 1.20.2, exact python -m mypy after current source installation in the CI import environment; all 19 configured sources.
Real wheel skill lifecycle passed Packaged Chat rebuilt and a real #5452 wheel installed in an isolated distribution; current release-workflow validator confirms all eight installed skills removed, including performance diagnosis. Shared workflow/skill contract is identical across these branches.

Maintainer review and hosted checks remain required. No runtime PR is self-merged.

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机

评审 exact head:90062f8cb33222430167f132f91bc9b6a4f21ad0。目标来自 #4381 的主动阻塞通知验收,以及 Goal Channel RFC 的投递、幂等、恢复和目标群契约。把阻塞摘要真正送到已授权群是有用增量;但“无须用户再打开 Tasks”要求持续刷新也能可靠送达,不能只验证第一条发送或把旧回执当作当前群成功。

改动思路

总体归属合理:沿用 Goal Channel / Lark provider 和私有 binding,复用 shared blocked-transition notice、bot 身份、membership、semantic key 和 readback;不另建默认 Capability,不把群回复变成 Todo 批准。两个自动通知开关独立、默认关闭,从已有 CLI、Chat API 和能力设置页配置,refresh-state 在已授权 sink 上执行;投递失败不应阻止安全 fallback。

正向路径是 canonical Todo/quota -> notice -> 私有绑定与 opt-in -> extension/bot/群检查 -> send -> message readback -> receipt -> 脱敏 status/UI 计数。receipt 是投递事实,不能取代 canonical Todo 恢复事实;同一 identity/revision 的去重必须再限定实际目标,并在恢复后允许新的一次 transition。Python 保留 provider IO 合理,但不应在 provider 里重新定义不完整的 Todo 生命周期。

具体改动

关键代码讲解

  • _active_notices(goal_channel_blocked_notice.py:47):合并既有 quota notices 和 agent/user Todo,调用共用 builder;但 81 行在去掉已投递项之前截取固定前八条,是下文饥饿的直接原因。
  • deliver_blocked_notices(同文件 113 行):序列化 binding mutation,校验 opt-in、外发授权、身份和群成员,再发送及回读。204–236 行以 identity/revision 查历史后直接复用 key,忽略该 key 的目标群;317–334 行只把 terminal 或 superseded Todo 作为恢复,漏掉未完成任务恢复为 open。
  • sync_blocked_notice_after_refresh / refresh-state hook:沿 canonical source route 收集 status/quota;异常产生非阻塞 delivery failure,不授予 Todo mutation 或新的外发权。关闭时应保持原 refresh 语义,抑制外部 sink 时不能进入消息写入。
  • configure_lark_goal_channel_automation / Chat configure handler:在同一个私有 owner 持久化独立 boolean 和启用 marker;CLI 显式 enable/disable,API 校验恰好一个 boolean,保留另一开关并返回读回。
  • GoalAutoNotifyToggle / goalChannelNotificationRowSchema:复用现有能力设置页,kind 选择 exclusive 请求字段,Zod、model、dashboard、双语文案及计数补齐;相关浏览器 fixture/scenario 和 Chat smoke 扩展了入口覆盖,双语 RFC 补 activation/disable/authority 说明。

本轮发布前检测到 head 更新,旧稿未发布;已在新 detached head 从当前 base 46a8c9b65ee8e2df5123a5762317e28b40d167fa 重新审阅全部 30 个 changed paths(+1297/-77)。新增 companion 将 chat activity 文件 100% 原样移入 presentation owner,更新实际 chat Agent 与测试导入;无残留旧模块导入,147 个顶层模块预算未放宽。registry I/O census 补两个新 lifecycle read sites 及位移;还包含 #5442/#5444 的原始署名测试修复,分别保留 admitted-write/drain 与 owner/receipt 断言。这些维护改动未修复下述 notice 生命周期问题,也没有引入新权限。

新 head 的六文件原生检查(四个 notice/Goal Channel 文件,加 tests/test_chat_activity.py 和 tests/control_plane/test_effect_runtime_integration.py)为 197 passed;tests/architecture/test_project_registry_io_census.py 7 passed,top-level-module-budget 通过,当前 local_authority_runtime.test.ts 35 passed,diff whitespace 通过。具体重跑命令:uv run --extra test python -m pytest -q tests/extensions/test_lark_goal_channel_blocked_notice.py tests/extensions/test_lark_goal_channel.py tests/extensions/test_lark_goal_channel_lifecycle.py tests/control_plane/test_blocked_transition_notice.py tests/test_chat_activity.py tests/control_plane/test_effect_runtime_integration.py。

同一份独立语义 oracle 已在新 head 再跑:实际 provider 函数和真实临时 binding IO,仅替换消息 transport,三个结果仍分别为 预期 2 / 实际 1、预期 9 / 实际 8、预期 2 / 实际 1,没有任何真实飞书调用。旧 head 的绿测、作者 Windows/CI 声明均不算本 head 的资格证据。这些临时 store 检查不是 installed/backend/packaged UI 的验收。

对主干的风险

  1. [P2] 普通恢复后同一阻塞不再通知(317–334 行)。顺序 blocked(reason R) -> open(reason cleared) -> blocked(reason R),首次有 verified delivered receipt。第二步共享 builder 已认为不再受阻,但 receipt 保持 delivered,因为 open 不在 terminal 集合;第三步仍复用该回执并报告 sent_verified。预期两次 transition 各发一次,实际总共仅一次。请根据明确、完整的 canonical 恢复事实退休旧 blocker receipt,区分 resumed 与 completed;不能仅凭分页缺失就解除。增加 open/reopen 回归,保留 done/reopen 与 missing-row 负例。

  2. [P2] 固定前八条永久饿死后续阻塞(81 行)。九个不同的 blocked Todos、稳定顺序,连续三次 refresh:预期九个最终各送一次,实际只有八次发送/八个回执。前八个即使全部 delivered,每次仍占据候选窗口,第九个永远进不了循环。请保留完整候选 frontier,把限额用于本次待处理的未送达效果,并给待续项真实 pending/truncated readback;不要简单解除单轮有界限制。至少验证 9+ 个阻塞、多轮不重复且最终送达。

  3. [P2] 换目标群后错误复用旧群回执(204–236 行)。既有 setup/attach 会保留 Goal receipts;群 A 首次 verified 后切到已授权且通过 membership 的群 B,同一 blocker 被 lookup 当作已有送达并跳过,B 返回 sent_verified/readback_verified=true,但 send targets 只有 A。key 创建时虽包含 chat_id,previous lookup 却没有目标约束。请把目标身份纳入回执匹配和存储,保留历史讨论与回执;B 应独立 send/readback,之后 B 的重复刷新才去重。补真实绑定切换的两目标测试。

语义与 CI 对齐

三个问题分别违反 #4381 的恢复、持久化投递/readback,以及 RFC 的目标相关语义幂等与持续通知要求。最小修复应留在已有 delivery owner,复用 canonical 状态归一化与共享 transition 规则,不新增平行 Todo authority、泛化框架或第二个 Python 决策源。当前扩展既有 Goal Channel opt-in/projection,provider receipt states 属本地投递生命周期;typed-state pass 要补明确恢复与 pending transition,而不靠 terminal 字符串子集假设。新 head 语义 advisory 已运行:11 个 changed source、3 个 carriers,均为 chat activity 的原样模块移动(COMMAND_VERBS/STEP_KINDS/STEP_STATES),沿用既有本地 presentation vocabulary,不新建共享协议。探针不覆盖动态字典/内联集合,不能证明 notice 语义安全。没有给 generic quota/todo 增加产品特定硬义务,通知不是审批或 must-attempt-work;用户“知情”与“操作”仍分开。

默认关闭的新 flag、CLI/API/UI 和 status 字段均有改动,因此不能只凭 false 宣称所有 feature-off 表面完全等价;当前 all-surface base/head pairing、packaged viewport 的 correction/reload、真实已安装 provider 与外部 readback 未完整独立验证,保留未验证,不以作者 browser fixture 或旧 head 记录代替。未查询、轮询或等待 GitHub CI。

我的整体评价

REQUEST_CHANGES:能力集成方向有价值,但长期持续通知和用户“当前群已送达”的可相信程度尚未达成。不是因为测试数量少,也不是因为没有等待 CI;197 项当前 Python 绿测及 35 项 typed claim 绿测仍没有覆盖三个实际通知生命周期反例。请修复恢复、候选公平性与目标回执匹配,并用上述语义序列扩展原生测试再运行四文件矩阵;然后补 affected feature-off 和已构建 UI 的真实交互/readback。相邻有界重构建议集中 provider receipt-selection/reconciliation seam,共用现有 canonical owner,不扩大成全量迁移。保留 unresolved 评审,未合并、未宣称 issue 全部验收完成。

English verdict: REQUEST_CHANGES - 90062f8: Three independently reproduced delivery gaps remain: blocked-open-blocked suppresses the new transition, a fixed first-eight window starves later blockers, and a channel switch reuses the old destination's receipt. The current 197-test Python matrix, 35-test typed claim suite and census/budget checks pass, but real binding IO with synthetic transport reproduces all three; live provider and packaged UI acceptance are not claimed.

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机

评审 exact head:d343dc36b69c08bc7f05e32f5436b091e93199e5。目标来自 #4381 的主动阻塞通知验收,以及 Goal Channel RFC 的投递、幂等、恢复和目标群契约。把阻塞摘要真正送到已授权群是有用增量;但“无须用户再打开 Tasks”要求持续刷新也能可靠送达,不能只验证第一条发送或把旧回执当作当前群成功。

改动思路

总体归属合理:沿用 Goal Channel / Lark provider 和私有 binding,复用 shared blocked-transition notice、bot 身份、membership、semantic key 和 readback;不另建默认 Capability,不把群回复变成 Todo 批准。两个自动通知开关独立、默认关闭,从已有 CLI、Chat API 和能力设置页配置,refresh-state 在已授权 sink 上执行;投递失败不应阻止安全 fallback。

正向路径是 canonical Todo/quota -> notice -> 私有绑定与 opt-in -> extension/bot/群检查 -> send -> message readback -> receipt -> 脱敏 status/UI 计数。receipt 是投递事实,不能取代 canonical Todo 恢复事实;同一 identity/revision 的去重必须再限定实际目标,并在恢复后允许新的一次 transition。Python 保留 provider IO 合理,但不应在 provider 里重新定义不完整的 Todo 生命周期。

具体改动

关键代码讲解

  • _active_notices(goal_channel_blocked_notice.py:47):合并既有 quota notices 和 agent/user Todo,调用共用 builder;但 81 行在去掉已投递项之前截取固定前八条,是下文饥饿的直接原因。
  • deliver_blocked_notices(同文件 112 行):序列化 binding mutation,校验 opt-in、外发授权、身份和群成员,再发送及回读。204–236 行以 identity/revision 查历史后直接复用 key,忽略该 key 的目标群;317–334 行只把 terminal 或 superseded Todo 作为恢复,漏掉未完成任务恢复为 open。
  • sync_blocked_notice_after_refresh / refresh-state hook:沿 canonical source route 收集 status/quota;异常产生非阻塞 delivery failure,不授予 Todo mutation 或新的外发权。关闭时应保持原 refresh 语义,抑制外部 sink 时不能进入消息写入。
  • configure_lark_goal_channel_automation / Chat configure handler:在同一个私有 owner 持久化独立 boolean 和启用 marker;CLI 显式 enable/disable,API 校验恰好一个 boolean,保留另一开关并返回读回。
  • GoalAutoNotifyToggle / goalChannelNotificationRowSchema:复用现有能力设置页,kind 选择 exclusive 请求字段,Zod、model、dashboard、双语文案及计数补齐;相关浏览器 fixture/scenario 和 Chat smoke 扩展了入口覆盖,双语 RFC 补 activation/disable/authority 说明。

本轮是对新 exact head 的重新评审,全部 31 个 changed paths(+1299/-78) 的整体结论没有继承旧批准。对比上一评审 90062f8cb33222430167f132f91bc9b6a4f21ad0,只有 test_interrupted_turn_continuation.py 的 +2/-1:导入并调用既有显式 read-only Todo helper。真实结算、幂等与 Todo bytes 断言未减弱;本轮这个 case 已实际通过。notice、CLI/API/UI、依赖及当前 base 46a8c9b65 均未变化,所以上一评审的整链代码分析和 companion 100% chat activity owner move、registry census、#5442/#5444 测试修复分析仍有效;它们不是通知缺陷的修复。

当前 head 独立重跑四个 notice/Goal Channel 文件加 interrupted continuation 为 106 passed。命令:uv run --extra test python -m pytest -q tests/extensions/test_lark_goal_channel_blocked_notice.py tests/extensions/test_lark_goal_channel.py tests/extensions/test_lark_goal_channel_lifecycle.py tests/control_plane/test_blocked_transition_notice.py tests/control_plane/test_interrupted_turn_continuation.py。上一 head 的197Python、35typed、census7和预算记录只对 byte-identical 路径复用,不冒充当前重新运行;本轮 diff whitespace 通过。

实际 provider + 真实临时 binding IO 的三个独立 oracle 已在新 head 再执行,仅替换消息 transport:结果仍为 预期2/实际1、预期9/实际8、预期2/实际1。没有真实飞书调用,正向绿测不能消除这三个持续通知缺陷。

对主干的风险

  1. [P2] 普通恢复后同一阻塞不再通知(317–334 行)。顺序 blocked(reason R) -> open(reason cleared) -> blocked(reason R),首次有 verified delivered receipt。第二步共享 builder 已认为不再受阻,但 receipt 保持 delivered,因为 open 不在 terminal 集合;第三步仍复用该回执并报告 sent_verified。预期两次 transition 各发一次,实际总共仅一次。请根据明确、完整的 canonical 恢复事实退休旧 blocker receipt,区分 resumed 与 completed;不能仅凭分页缺失就解除。增加 open/reopen 回归,保留 done/reopen 与 missing-row 负例。

  2. [P2] 固定前八条永久饿死后续阻塞(81 行)。九个不同的 blocked Todos、稳定顺序,连续三次 refresh:预期九个最终各送一次,实际只有八次发送/八个回执。前八个即使全部 delivered,每次仍占据候选窗口,第九个永远进不了循环。请保留完整候选 frontier,把限额用于本次待处理的未送达效果,并给待续项真实 pending/truncated readback;不要简单解除单轮有界限制。至少验证 9+ 个阻塞、多轮不重复且最终送达。

  3. [P2] 换目标群后错误复用旧群回执(204–236 行)。既有 setup/attach 会保留 Goal receipts;群 A 首次 verified 后切到已授权且通过 membership 的群 B,同一 blocker 被 lookup 当作已有送达并跳过,B 返回 sent_verified/readback_verified=true,但 send targets 只有 A。key 创建时虽包含 chat_id,previous lookup 却没有目标约束。请把目标身份纳入回执匹配和存储,保留历史讨论与回执;B 应独立 send/readback,之后 B 的重复刷新才去重。补真实绑定切换的两目标测试。

语义与 CI 对齐

三个问题分别违反 #4381 的恢复、持久化投递/readback,以及 RFC 的目标相关语义幂等与持续通知要求。最小修复应留在已有 delivery owner,复用 canonical 状态归一化与共享 transition 规则,不新增平行 Todo authority、泛化框架或第二个 Python 决策源。当前扩展既有 Goal Channel opt-in/projection,provider receipt states 属本地投递生命周期;typed-state pass 要补明确恢复与 pending transition,而不靠 terminal 字符串子集假设。上一90062 head 的语义 advisory 已运行并对 byte-identical 路径复用:11 个 changed source、3 个 carriers,均为 chat activity 的原样模块移动(COMMAND_VERBS/STEP_KINDS/STEP_STATES),沿用既有本地 presentation vocabulary,不新建共享协议。探针不覆盖动态字典/内联集合,不能证明 notice 语义安全。没有给 generic quota/todo 增加产品特定硬义务,通知不是审批或 must-attempt-work;用户“知情”与“操作”仍分开。

默认关闭的新 flag、CLI/API/UI 和 status 字段均有改动,因此不能只凭 false 宣称所有 feature-off 表面完全等价;当前 all-surface base/head pairing、packaged viewport 的 correction/reload、真实已安装 provider 与外部 readback 未完整独立验证,保留未验证,不以作者 browser fixture 或旧 head 记录代替。未查询、轮询或等待 GitHub CI。

我的整体评价

REQUEST_CHANGES:能力集成方向有价值,但长期持续通知和用户“当前群已送达”的可相信程度尚未达成。不是因为测试数量少,也不是因为没有等待 CI;本轮106项当前绿测仍没有覆盖三个实际通知生命周期反例;旧head的其他绿测也不能替代当前反例。请修复恢复、候选公平性与目标回执匹配,并用上述语义序列扩展原生测试再运行四文件矩阵;然后补 affected feature-off 和已构建 UI 的真实交互/readback。相邻有界重构建议集中 provider receipt-selection/reconciliation seam,共用现有 canonical owner,不扩大成全量迁移。保留 unresolved 评审,未合并、未宣称 issue 全部验收完成。

English verdict: REQUEST_CHANGES - d343dc3: Three independently reproduced delivery gaps remain: blocked-open-blocked suppresses the new transition, a fixed first-eight window starves later blockers, and a channel switch reuses the old destination's receipt. The current 106-test notice/continuation matrix passes and prior unchanged-path evidence is explicitly source-pinned, but real binding IO with synthetic transport reproduces all three; live provider and packaged UI acceptance are not claimed.

@mergify

mergify Bot commented Oct 2, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @jackie-cqz.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 2, 2026
@jackie-cqz
jackie-cqz force-pushed the codex/fix-blocked-notice-delivery branch from d343dc3 to 6d81d4f Compare October 2, 2026 12:33
@mergify mergify Bot removed the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 2, 2026
@jackie-cqz

jackie-cqz commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

The three review findings are addressed in current head 99155a77d38bd023d8a5243bee118e3a8bdb0c0e:

  • Explicit open recovery retires the previous receipt as resumed. Repeated blocked/open/blocked cycles use durable generations, including a fixed clock and reordered receipt storage. Missing or unknown source rows do not imply recovery.
  • The complete frontier is retained; only pending effect attempts consume the eight-effect batch. New effects run before retries. Regressions cover 17 blockers and persistent failures without starving later candidates.
  • Direct and named destination changes require separate send/readback and current-target public totals. Historic receipts remain private history and cannot certify a new channel.

Default-off/suppressed bindings retain byte parity and invoke no transport. Human-gate-only status reads do not enter the blocked-notice target lookup. Earlier Linux matrix plus real CLI continuation: 143 passed on the preceding head; its scope is retained. Packaged Chat build and desktop/mobile settings recovery/readback passed. Windows activity/update/managed-install fixture tests: 57 passed, 4 platform skips. Live Lark delivery is unqualified without an authorized group; hosted CI is rerunning on this head.

Please re-review the current head. This runtime PR is left for maintainer review and merge.

Latest main edb78b918 already ships the presentation-owner migration. The duplicate branch migration is removed; the Windows UTF-8 fixture fix remains. Current activity/census/module-budget checks: 43 passed.

The current head also incorporates upstream #5467 release-validation repair. Exact kernel mypy passes in the CI source-install/import environment. A rebuilt packaged wheel passes the current release-workflow skill install/uninstall validator, removing all eight installed skills. Hosted CI is rerunning; these local checks do not certify its completion.

Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
(cherry picked from commit 01c00f727e999f5328407680a4fbb99f55ec9bcb)
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
Signed-off-by: jackie-cqz <2557911191@qq.com>
@jackie-cqz
jackie-cqz force-pushed the codex/fix-blocked-notice-delivery branch from feae790 to 99155a7 Compare October 2, 2026 13:52

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · GPT-5 · OpenAI(self-reported;不是身份认证或独立模型溯源)

动机

评审 exact head:99155a77d38bd023d8a5243bee118e3a8bdb0c0e,基线 4fc30f185e6fbe688b1f0a16b0f7bdafe2666ba9。依据 #4381 和改动前 Goal Channel RFC:把受阻事实送到已有授权群,免去反复打开 Tasks。重点是恢复后再受阻、多轮队列公平性及当前目标真正送达,不能只验证首次发送。

固定规范文本为 docs/architecture/rfcs/goal-channel-collaboration-v0.md、revision 4fc30f185e6fbe688b1f0a16b0f7bdafe2666ba9。按原章节 criterion 对照:Goals 的 canonical ownership/显式 idempotent readback 由 delivery/configure 与真实 IO 验证;Idempotency And Cooldown 的当前目标、retry 去重由三项历史反例和冻结时钟回归验证;Security And Privacy 的 bot/member/readback、私有 receipt 与不授予状态权限由原生负例验证。Validation 中 default-off、suppression、无重复已验证,但真实授权 Lark 服务及 native Windows 留作 #4381 的 deferred 验收,不用新增 RFC 文案覆盖缺口。

改动思路

沿用现有 Goal Channel/provider、私有 binding 和 shared blocked-transition builder。canonical Todo 仍拥有工作状态,provider 只管理投递回执,不新增 Todo 决策源。独立开关绑定所选 Goal;开启不能绕过 extension、bot/app、membership 和外部 sink 授权。发送先留 pending,再校验/readback,失败和未校验明确保留,下轮有界继续。相邻重构把 recovery、frontier 和目标匹配集中在原 delivery owner,专用 Lark transport 留在既有 Python provider,不扩大为语言迁移或新 capability。

具体改动

检查完整 32 paths,+1688/-83:provider/refresh/CLI/API、现有 dashboard 设置与读模型、双语 RFC、持久化/browser 回归,以及 census/fixture 的有界伴随维护。

关键代码:

  • _active_notices:保留完整 canonical/fallback frontier,canonical 同 ID 事实覆盖 fallback;已核验 canonical summary 使用 item_limit=None,不是拿展示分页当完整状态。
  • _reconcile_receipts / _receipt_for_notice:明确 open 恢复、终态解除与原因替换,按 generation 和当前目标匹配;缺行不等于恢复,历史回执保留。
  • deliver_blocked_notices:delivered 条目不消耗本轮发送额度;pending 按尝试次数调度,持续失败也不永久饿死新条目。先留待续事实,再认证、发送、读回。
  • configure_lark_goal_channel_automation / HTTP handler:独立 boolean 与 marker,保留另一开关;恰好一个布尔项,错误输入或缺 extension 不假装成功。
  • GoalAutoNotifyToggle / notification projection:复用用户已打开的 Goal 设置,补错误、重试、键盘操作和当前目标的 verified/unverified/resolved 计数,不增加重复目标输入或新确认步骤。

独立验证:rebased 当前 head 重跑原11文件,加上 main 新整合的 CLI diagnostics/dashboard command 两文件,341 passed、2 skipped;Windows-only 边界保留跳过。原三个反例实际在旧候选 d343dc36b69c08bc7f05e32f5436b091e93199e5 和当前 99155a77 重跑:blocked→open→同因 blocked 1→2 次发送;九个稳定 blocker 多轮 8→9;授权群 A→B 1→2。期望预先来自恢复、公平性和目标契约,不从输出反推;真实 binding IO,只有 Lark transport 为 synthetic,真实外发为零。

dashboard build、构建产物的 blocked-notice-settings browser scenario、真实 HTTP chat-server smoke 在先前 feae79069cccdd2e63076051001be957f9d5977b 通过。rebase 后整体32文件 PR patch SHA-256 仍为 fdc107fb02aef63cab02d87d3a324fbd90788c239e61806dd1bb33d8d689ba56,每个改动文件 blob 相同,UI/backend/build 源码均未变;只新增 main 的 release assertion 和两项 host 测试,已读 diff 并重跑,故复用旧构建证据而不冒称新 head 重建。查看桌面和390×844 viewport;失败申请仍未选中,重试成功,reload 与键盘取消读回一致。browser API projection 为 fixture,不称为真实服务证据。当前 head 再跑真实 HTTP server:blocked flag true→false 持久化、readback_verified=true、fresh projection false,人工 flag 不变;无 extension 的 enable 返回400且 binding 不变,没有 provider/Agent 调用。

base/head 同夹具原人工通知 direct/lifecycle 完整输出一致;未配置/配置关闭均零 provider 调用、binding bytes 不变。开启、抑制、缺行、冻结时钟、字典顺序、持续失败及当前目标读回也有原生负例覆盖。

对主干的风险

没有发现剩余可复现 PR 阻塞,原三项 P2 已逐项复核修复。通知是“知情”不是“操作”,不改 Todo、授予权限、结算 Goal 或新增 must_attempt_work。当前目标不能借旧目标的成功回执。局部 BlockedNoticeReceiptState Enum 明确 pending、sent_unverified、delivered、resumed、resolved、superseded;无 substring denylist 代替 canonical 分类。

默认关闭指投递不自动启用,不承诺隐藏显式设置页里的功能可用性。status 的 false availability 和 optional counters、既有 Zod/model 的兼容读回已单独比较并在双语 RFC/界面说明;没有把这些字段删掉后宣称整份 projection 字节相等。安装、可用性、新字段或控件不授权投递,原人工路径的权限和效果保持一致。

语义与 CI 对齐

development advisory 的一个新 Enum carrier 属原 provider 局部投递生命周期,不是新共享 Todo vocabulary;full semantic drift、边界扫描和直接检查通过。canary 5 direct通过,19 selected中18通过、1失败:examples/install-local-smoke.py line457 的既有 skill 文案断言在基线独立重跑同样失败,完整堆栈与断言一致。安装 smoke/被检查文案未由此 PR 改动,provider/HTTP/UI 不变量另外通过。保留失败和原始红色 gate,不称 canary 全绿或安装验收完成。

未查询、轮询或等待 GitHub CI。真实 Lark 服务、native Windows 和 owner 当前安装应用未验证;这是源代码及构建产物的有界增量,不关闭 #4381 所有 sink/上线验收。

我的整体评价

APPROVE:三个原反例在旧版确实失败,当前得到预先定义的结果;关闭路径、真实配置读回和构建界面恢复链也补齐。相邻有界重构已集中 receipt-selection/reconciliation,保留 canonical owner。实际服务/平台和已有安装文案失败继续作为明确风险。维护者可依既有流程决定合并;本评审未合并,也不把 review、绿测或通知回执当整个 Goal 完成。

English verdict: APPROVE — 99155a7. The original recovery, fairness and destination-readback counterexamples are independently resolved. Native tests, real HTTP configuration readback and built UI checks pass. An unchanged install assertion is independently reproduced on the baseline; live Lark and native Windows acceptance are not claimed. No merge performed.

@huangruiteng
huangruiteng merged commit 1412d12 into loopx-project:main Oct 2, 2026
28 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants